﻿
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Runtime.CompilerServices;


namespace DailyFx.UI
{
 	 /// <summary>
    ///		Represents a collection that is observable.
    /// </summary>
    public interface IBindableCollection<T> : IList<T>, INotifyPropertyChangedEx
	{
        /// <summary>
        ///   Adds the range.
        /// </summary>
        void AddRange ( IEnumerable<T> items );

        /// <summary>
        ///   Removes the range.
        /// </summary>
        void RemoveRange ( IEnumerable<T> items );
    }


    /// <summary>
    ///		collection  that supports automatic UI thread marshalling.
    /// </summary>
#if !SILVERLIGHT && !WinRT
    // [Serializable]
#endif
    public class BindableCollection<T> : ObservableCollection<T>, IBindableCollection<T>
	{

        /// <summary>
        ///   Initializes a new instance of the <see cref = "DailyFx.UI.BindableCollection{T}" /> class.
        /// </summary>
        public BindableCollection () 
		{
            IsNotifying = true;
        }

        /// <summary>
        ///   Initializes a new instance of the <see cref = "DailyFx.UI.BindableCollection{T}" /> class.
        /// </summary>
        public BindableCollection(IEnumerable<T> collection) 
			: base(collection) 
		{
            this.IsNotifying = true ;
        }

#if !SILVERLIGHT && !WinRT
       // [field: NonSerialized]
#endif
        bool isNotifying; //serializator try to serialize even autogenerated fields

        /// <summary>
        ///   Enables/Disables property change notification.
        /// </summary>
#if !WinRT
        [Browsable(false)]
#endif
        public bool IsNotifying
		{
            get { 
				return this.isNotifying;
			}
            set { 
				this.isNotifying = value;
			}
        }

        /// <summary>
        ///   Notifies subscribers of the property change.
        /// </summary>
#if WinRT || NET45
        public virtual void RaisePropertyChanged ([CallerMemberName]string propertyName = "") 
#else
        public virtual void RaisePropertyChanged (string propertyName) 
		
#endif
		{
            if( IsNotifying )
                Execution.OnUIThread( () => OnPropertyChanged( new PropertyChangedEventArgs(propertyName)) );
        }

        /// <summary>
        ///   Raises a change notification indicating that all bindings should be refreshed.
        /// </summary>
        public void Refresh() 
		{
            Execution.OnUIThread( () => 
			{
                OnPropertyChanged	( new PropertyChangedEventArgs("Count") );
                OnPropertyChanged	( new PropertyChangedEventArgs("Item[]") );
                OnCollectionChanged	( new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset) );
            });
        }

        /// <summary>
        ///   Inserts the item to the specified position.
        /// </summary>
        protected override sealed void InsertItem ( int index, T item )
		{
            Execution.OnUIThread( () => InsertItemImpl(index, item) );
        }

        /// <summary>
        ///   Exposes the base implementation of the <see cref = "InsertItem" /> function.
        /// </summary>
        /// <remarks>
        ///   Used to avoid compiler warning regarding unverifiable code.
        /// </remarks>
        protected virtual void InsertItemImpl ( int index, T item)
		{
            base.InsertItem ( index, item );
        }

#if NET || WP8 || WinRT

		/// <summary>
		///		Moves the item within the collection.
		/// </summary>
        protected sealed override void MoveItem ( int oldIndex, int newIndex )
		{
            Execution.OnUIThread( () => MoveItemImpl (oldIndex, newIndex) );
        }

        /// <summary>
        /// Exposes the base implementation fo the <see cref="MoveItem"/> function.
        /// </summary>
        /// <remarks>
		///		Used to avoid compiler warning regarding unverificable code.
		///	</remarks>
        protected virtual void MoveItemImpl (int oldIndex, int newIndex)
		{
            base.MoveItem(oldIndex, newIndex);
        }
#endif

        /// <summary>
        ///   Sets the item at the specified position.
        /// </summary>
        protected override sealed void SetItem( int index, T item ) 
		{
            Execution.OnUIThread( () => SetItemImpl(index, item) );
        }

        /// <summary>
        ///   Exposes the base implementation of the <see cref = "SetItem" /> function.
        /// </summary>
        /// <remarks>
        ///   Used to avoid compiler warning regarding unverifiable code.
        /// </remarks>
        protected virtual void SetItemImpl (int index, T item)
		{
            base.SetItem ( index, item );
        }

        /// <summary>
        ///   Removes the item at the specified position.
        /// </summary>
        protected override sealed void RemoveItem ( int index )
		{
            Execution.OnUIThread( () => RemoveItemImpl(index) );
        }

        /// <summary>
        ///   Exposes the base implementation of the <see cref = "RemoveItem" /> function.
        /// </summary>
        /// <remarks>
        ///   Used to avoid compiler warning regarding unverifiable code.
        /// </remarks>
        protected virtual void RemoveItemImpl ( int index)
		{
            base.RemoveItem ( index );
        }

        /// <summary>
        ///   Clears the items contained by the collection.
        /// </summary>
        protected override sealed void ClearItems ()
		{
            Execution.OnUIThread ( ClearItemsImpl );
        }

        /// <summary>
        ///   Exposes the base implementation of the <see cref = "ClearItems" /> function.
        /// </summary>
        /// <remarks>
        ///   Used to avoid compiler warning regarding unverifiable code.
        /// </remarks>
        protected virtual void ClearItemsImpl()
		{
            base.ClearItems();
        }

        /// <summary>
        ///   Raises the <see cref = "E:System.Collections.ObjectModel.ObservableCollection`1.CollectionChanged" /> event with the provided arguments.
        /// </summary>
        protected override void OnCollectionChanged ( NotifyCollectionChangedEventArgs e)
		{
            if ( IsNotifying ) 
                base.OnCollectionChanged (e);
        }

        /// <summary>
        ///   Raises the PropertyChanged event with the provided arguments.
        /// </summary>
        protected override void OnPropertyChanged ( PropertyChangedEventArgs e)
		{
            if (IsNotifying) 
                base.OnPropertyChanged (e);
        }

        /// <summary>
        ///   Adds the range.
        /// </summary>
        public virtual void AddRange ( IEnumerable<T> items ) 
		{
            Execution.OnUIThread( () => 
			{
                var previousNotificationSetting = IsNotifying;
				this.IsNotifying = false;
                
				var index = Count;
                foreach ( var item in items )
				{
                    InsertItemImpl ( index, item);
                    index++;
                }
                
				this.IsNotifying = previousNotificationSetting;
				
				// this.Refresh();
				OnPropertyChanged	( new PropertyChangedEventArgs("Count") );
				OnPropertyChanged	( new PropertyChangedEventArgs("Item[]") );
				OnCollectionChanged	( new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset) );
            });
        }

        /// <summary>
        ///   Removes the range.
        /// </summary>
        public virtual void RemoveRange ( IEnumerable<T> items )
		{
            Execution.ToUIThread(() => 
			{
                var previousNotificationSetting = IsNotifying;
                this.IsNotifying = false;
                
				foreach ( var item in items )
				{
                    var index = IndexOf(item);
                    if (index >= 0) 
					{
                        RemoveItemImpl ( index );
                    }
                }
                this.IsNotifying = previousNotificationSetting;
				
				// this.Refresh();
                OnPropertyChanged	( new PropertyChangedEventArgs("Count") );
                OnPropertyChanged	( new PropertyChangedEventArgs("Item[]") );
                OnCollectionChanged	( new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset) );
            });
        }

    }
}